package com.xinxing.learning.crypto.advice;

import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xinxing.learning.crypto.annotation.EncryptionAnnotation;
import com.xinxing.learning.crypto.entity.RequestBase;
import com.xinxing.learning.crypto.entity.Result;
import com.xinxing.learning.crypto.exception.CryptoException;
import com.xinxing.learning.crypto.keyresolver.InterfaceKeyresolver;
import com.xinxing.learning.crypto.util.AESUtil;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.lang.reflect.Type;

/**
 * 响应加密
 */
@ControllerAdvice
public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Result<?>> {

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private AESUtil aesUtil;

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        ParameterizedTypeImpl genericParameterType = (ParameterizedTypeImpl) returnType.getGenericParameterType();
        // 如果直接是Result并且有加密注解，则处理
        if (genericParameterType.getRawType() == Result.class && returnType.hasMethodAnnotation(EncryptionAnnotation.class)) {
            return true;
        }

        // 如果不是ResponseBody或者是Result，则放行
        if (genericParameterType.getRawType() != ResponseEntity.class) {
            return false;
        }

        // 如果是ResponseEntity<Result>并且有加密注解，则处理
        for (Type type : genericParameterType.getActualTypeArguments()) {
            if (((ParameterizedTypeImpl) type).getRawType() == Result.class && returnType.hasMethodAnnotation(EncryptionAnnotation.class)) {
                return true;
            }
        }

        return false;
    }

    @SneakyThrows
    @Override
    public Result<?> beforeBodyWrite(Result<?> body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {

        // 真实数据
        Object data = body.getData();

        // 如果data为空，直接返回
        if (data == null) {
            return body;
        }

        // 如果是实体，并且继承了Request，则放入时间戳
        if (data instanceof RequestBase) {
            ((RequestBase) data).setCurrentTimeMillis(System.currentTimeMillis());
        }

        // Teacher(name=张三, age=18, birthday=Sat Jul 08 18:46:56 CST 2023) 中的birthday 会被格式化成时间戳
        // {"birthday":1688813216000,"currentTimeMillis":1688813291586,"name":"张三","age":18}
        // String dataText = JSONUtil.toJsonStr(data);
        String dataText = objectMapper.writeValueAsString(data);

        // 如果data为空，直接返回
        if (StringUtils.isBlank(dataText)) {
            return body;
        }

        // 如果位数小于16，报错
        if (dataText.length() < 16) {
            throw new CryptoException("加密失败，数据小于16位");
        }

        EncryptionAnnotation methodAnnotation = returnType.getMethodAnnotation(EncryptionAnnotation.class);
        Class<? extends InterfaceKeyresolver> aClass = methodAnnotation.keyResolver();
        InterfaceKeyresolver interfacekeyresolver = aesUtil.getInterfacekeyresolver(methodAnnotation.keyResolver());
        String encryptText = interfacekeyresolver.encryptHex(dataText);
        // String encryptText = AESUtil.encryptHex(dataText);

        return Result.builder()
                .status(body.getStatus())
                .data(encryptText)
                .message(body.getMessage())
                .build();
    }
}